#include <sys/epoll.h>

#include <stdio.h>
#include <unistd.h>

#include "CBSocket.h"

#include "AbstractCallback.h"

cbsocket::CBSocket::CBSocket():
callbacks(),
epollfd(-1),
running(false)
{
    epollfd = epoll_create(16);
    if(epollfd < 0)
        perror("epoll_create");
}

cbsocket::CBSocket::~CBSocket()
{
    for(std::set<AbstractCallback*>::iterator p = callbacks.begin(); p != callbacks.end(); ++p)
        delete *p;
    close(epollfd);
}

bool cbsocket::CBSocket::addSocket(int fd, cbsocket::AbstractCallback* callback)
{
    epoll_event event;
    event.events = EPOLLIN | EPOLLOUT | EPOLLRDHUP | EPOLLET;
    event.data.ptr = callback;
    int err = epoll_ctl(epollfd, EPOLL_CTL_ADD, fd, &event);
    if(err)
    {
        perror("epoll_ctl");
        return false;
    }
    else
    {
        callbacks.insert(callback);
        return true;
    }
}

void cbsocket::CBSocket::run()
{
    running = true;
    while(running)
    {
        epoll_event events[64];
        int event_count = epoll_wait(epollfd, events, 64, 1000);
        if(event_count < 0)
        {
            perror("epoll_wait");
            running = false;
        }
        for(int i = 0; i < event_count; ++i)
        {
            AbstractCallback *callback = static_cast<AbstractCallback*>(events[i].data.ptr);
            if(events[i].events & EPOLLIN)
                callback->dataReady();
            if(events[i].events & EPOLLOUT)
                callback->writeReady();
            if(events[i].events & EPOLLRDHUP)
                callback->dataStop();
            if(events[i].events & EPOLLERR)
                callback->error();
            if(events[i].events & EPOLLHUP)
                callback->hangUp();
            if(callback->needCleanUp())
            {
                epoll_ctl(epollfd, EPOLL_CTL_DEL, callback->getFd(), NULL);
                callbacks.erase(callback);
                delete callback;
            }
        }
    }
}

void cbsocket::CBSocket::stop()
{
    running = false;
}
